#include "../include/Vector.h"
#include <cassert>
#include <cmath>
#include "../include/Matrix.h"

Vector::Vector(int dim)
{
    assert(dim > 0);
    nDim = dim;
    m_type = COLUMNVECTOR;
    data = new real[nDim];
    for(int i = 0; i < nDim; ++i)
        data[i] = 0.0;
}
Vector::Vector(const Vector& v)
{
    nDim = v.dim();
    m_type = v.type();
    data = new real[nDim];
    for(int i = 0; i < nDim; ++i)
        data[i] = v(i);
}

Vector::~Vector()
{
    delete [] data;
}

real& Vector::at(int idx)
{
    assert(idx > 0 && idx <= nDim);
    return data[idx-1];

}
const real Vector::at(int idx)const
{
    assert(idx > 0 && idx <= nDim);
    return data[idx-1];
}

real& Vector::operator()(int idx)
{
    return at(idx);
}

const real Vector::operator()(int idx)const
{
    return at(idx);
}

Vector Vector::operator+(const Vector& v)const
{
    assert(v.dim() == dim() && v.type() == type());
    Vector resp(dim());
    for(int i=1; i <= dim(); ++i)
        resp(i) = at(i) + v(i);

    return type() != resp.type()? resp.transpose() : resp;
}
Vector Vector::operator-(const Vector& v)const
{
    assert(v.dim() == dim() && v.type() == type());
    Vector resp(dim());
    for(int i=1; i <= dim(); ++i)
        resp(i) = at(i) - v(i);

    return type() != resp.type()? resp.transpose() : resp;
}
Vector Vector::operator-()const
{
    Vector resp(dim());
    for(int i=1; i <= dim(); ++i)
        resp(i) = -at(i);

    return type() != resp.type()? resp.transpose() : resp;
}
Matrix Vector::operator*(const Vector& v)const
{
    assert(v.type() != type() && v.dim() == dim());
    Matrix m;
    if(type() == LINEVECTOR)
    {
        m = Matrix(1,1);
        m(1,1) = Vector::dot(*this, v);
    }
    else
    {
        m = Matrix(dim(),dim());
        for(int i = 1; i <= dim(); ++i)
            for(int j = 1; j <= dim(); ++j)
                m(i,j) = at(i)*v(j);
    }

    return m;
}

Vector Vector::operator*(real a)const
{
    Vector resp(dim());
    for(int i=1; i <= dim(); ++i)
        resp(i) = at(i)*a;

    return type() != resp.type()? resp.transpose() : resp;
}
Vector Vector::operator/(real a)const
{
    Vector resp(dim());
    for(int i=1; i <= dim(); ++i)
        resp(i) = at(i)/a;

    return type() != resp.type()? resp.transpose() : resp;
}
Vector operator*(real a, const Vector& v)
{
    return v*a;
}
const Vector& Vector::operator=(const Vector& v)
{
    if(dim() != v.dim())
    {
        delete [] data;
        nDim = v.dim();
        data = new real[nDim];
    }

    for(int i = 1; i <= nDim; ++i)
        at(i) = v(i);

    m_type = v.type();

    return *this;
}

void Vector::swap(int i, int j)
{
    real t = at(i);
    at(i) = at(j);
    at(j) = t;
}

const int Vector::dim()const
{
    return nDim;
}

TypeVector Vector::type()const
{
    return m_type;
}
const Vector&  Vector::transpose()
{
    m_type = (m_type == LINEVECTOR?COLUMNVECTOR:LINEVECTOR);

    return *this;
}

real Vector::length ()
{
    return sqrtf(dot(*this,*this));
}

std::ostream& operator<<(std::ostream& out, const Vector& v)
{
    out << (v.type() == LINEVECTOR? "L" : "C" ) << "::[";
    for(int i = 1; i < v.dim(); ++i)
        out << "\t" << v(i) << ",";
    out << "\t" << v(v.dim()) << "\t]";
    return out;
}

real Vector::dot(const Vector& v1, const Vector& v2)
{
    assert(v1.dim() == v2.dim());
    real r = 0.0;
    for(int i = 1; i <= v1.dim(); ++i)
        r+= v1(i)*v2(i);
    return r;
}

void Vector::set(real * d)
{
    for(int i = 0; i < dim(); ++i)
        data[i] = d[i];
}

void Vector::set(real v)
{
    for(int i = 0; i < dim(); ++i)
        data[i] = v;
}
